<?php
/**
 * Easthv Zend Framework Project
 *
 *
 * LICENSE:
 *
 * @category   Zend
 * @package    Zend_Easthv
 * @copyright  Copyright (c) 2009-2010 Easthv Team @ East Hui Van Business Consulting Co., Ltd.(http://www.easthv.com)
 * @license    http://framework.zend.com/license/   BSD License
 * @version    1.0
 * @link       http://www.easthv.com
 * @since      1.0
*/

/**
 * Jing Dong OCR class
 *
 * @category   Zend
 * @package    Zend_Easthv
 * @copyright  Copyright (c) 2009-2010 Easthv Team @ East Hui Van Business Consulting Co., Ltd.(http://www.easthv.com)
 * @license    http://framework.zend.com/license/   BSD License
 * @version    1.0
 * @link       http://www.easthv.com
 * @since      1.0
 * @deprecated
 */
class Zend_Easthv_Ocr_Jingdong
{

	public static $debug = false;

	public static $config = array(
		'numberWidth' => 10, //数字宽度
		'numberHeight' => 11, //数字高度
		'spacing' => 1, //数字间距
		'pointWidth' => 4, //小数点宽度
		'marginLeft' => 20, 
		'marginTop' => 4,
		'standardNumberPath' => '/Jingdong/',
	);

    /**
     * 标准图片解析结果
     */
	private static $_standardNumbers;

    /**
     * OCR 京东商城价格图片
     * 
     * @param string $imagePath
     * @return string
     */
	public static function ocrPriceImage($imagePath)
	{
		self::init();

		$isUrlFile = stripos($imagePath, 'http://') === false ? false : true;

		if($isUrlFile === false && !file_exists($imagePath)) {
			throw new Exception('Input ocr image not found');
		}

		$imagePixelsArray = self::_getImagePixelsArray($imagePath);

		self::$debug === true ? self::printNumberArray(self::_numberArrayToBitArray($imagePixelsArray)) : '';

		$numbersArray = self::_splitToNumbersArray($imagePixelsArray);

		$result = array();

		foreach($numbersArray as $numberArray) {
			$result[] .= self::ocrNumberArray($numberArray);
			//self::printNumberArray(self::_numberArrayToBitArray($numberArray));
		}
		$last = array_pop($result);
		$llast = array_pop($result);

		return implode('', $result) . ".$llast$last";
	}

    /**
     * OCR Number Array
     * 
     * @param array
     * @return string
	 */
	public static function ocrNumberArray($numberArray, $standardNumbers = null)
	{
		$numberString = self::_numberArrayToString(
			self::_numberArrayToBitArray($numberArray)
		);

		$standardNumbers = $standardNumbers ? $standardNumbers : self::$_standardNumbers;

		$similar = array();
		$max = 0;
		$result = 0;

		foreach($standardNumbers as $key => $standardNumber){
			$similar[$key] = similar_text($numberString, $standardNumber);
			if($similar[$key] > $max) {
				$max = $similar[$key];
				$result = $key;
			}
		}

		//最大相似度未满95，用新算法重新识别
		if($max < 95) {
			$similar = array();
			$max = 0;
			$result = 0;
			foreach($standardNumbers as $key => $standardNumber){
				$similar[$key] = self::_getBitArraySimilarPersent(self::_numberArrayToBitArray($numberArray), self::_stringToNumberArray($standardNumber));
				if($similar[$key] > $max) {
					$max = $similar[$key];
					$result = $key;
				}
			}
		}

		if(self::$debug === true) {
			echo '<pre>Orc Log:</pre>';
			self::printNumberArray(self::_numberArrayToBitArray($numberArray));
			foreach($standardNumbers as $key => $standardNumber){
				$similar[$key] = similar_text($numberString, $standardNumber);
				echo '<pre>Compare to ' . $key . ': ' . $similar[$key] . '</pre>';
				if($similar[$key] > $max) {
					$max = $similar[$key];
					$result = $key;
				}
			}
		}

		return (string)$result;
	}

	/**
	 * 逐像素比较两个像素数组是否一致，返回一致像素所占百分比
	 * 
	 * 
	 * @param array
	 * @param array
	 * @return float
	 */
	private static function _getBitArraySimilarPersent($numberArray, $standardNumberArray)
	{

		$bitCount = 0;
		$sameBitCount = 0;
		foreach($standardNumberArray as $i => $numberLine){
			foreach($numberLine as $j => $numberBit) {
				if($numberBit == 1) {
					$bitCount++;
				}

				if($numberBit == $numberArray[$i][$j]) {
					$sameBitCount++;
				}
			}
		}

		return $sameBitCount / $bitCount;
	}

	/*
	 * 旧版本，用字符串相似度

	 */

	/**
	 * Print Number Array for debug
	 * 
	 * @param array
	 * @return void
	 */
	public static function printNumberArray($numberArray)
	{
		if(!$numberArray) {
			return;
		}

		$heightCount = count($numberArray[0]);
		$widthCount = count($numberArray);

		echo '<div style="font-family:Lucida Console, Monaco, Courier New, mono, monospace;">';
		echo '<br />';
		for($i = 0; $i < $heightCount; $i++) {
			for($j = 0; $j < $widthCount; $j++) {
				echo $numberArray[$j][$i] == '1' ? '◎' : '◯';
	//			echo $numberArray[$j][$i];
			}
			echo '<br />';
		}
		echo '<br /></div>';	
	}

    /**
     * 将整张图片的数组按数字位数分割,小数点被剔除，数组的最后两个一定是小数点后两位
     * 
     * @param array
     * @return array
     */
	private static function _splitToNumbersArray($imagePixelsArray)
	{
		$digits = self::_getNumberDigits($imagePixelsArray);
		$imagePixelsWidth = count($imagePixelsArray);
		$numberWidth = self::$config['numberWidth'];
		$pointWidth = self::$config['pointWidth'];
		$spacing = self::$config['spacing'];
		
		$numbers = array();
		for($i = 0; $i < $digits; $i++){
			$number = array();
			for($j=0; $j < $numberWidth; $j++){
				$offset = $i * $numberWidth + $j + $i * $spacing;
				$number[] = $imagePixelsArray[$offset];
			}
			$numbers[] = $number;
		}

		//小数点后两位
		$lastline = $numberWidth * 2 + $spacing;
		$lastline = count($imagePixelsArray) - $lastline;

		$number = array();
		for($i = $lastline; $i < $lastline + $numberWidth; $i++){
			$number[] = $imagePixelsArray[$i];
		}
		$numbers[] = $number;


		$lastline = $numberWidth;
		$lastline = $imagePixelsWidth  - $lastline;

		$number = array();
		for($i = $lastline; $i < $lastline + $numberWidth; $i++){
			$number[] = $imagePixelsArray[$i];
		}
		$numbers[] = $number;

		return $numbers;	
	}

    /**
	 * 将整张图片按像素读入一个二维数组，数组第一维是列数，第二维是当前像素16进制颜色
	 * 会将图片的冗余部分移除 （上下左右留白）
     * 
     * @param string
     * @return array
     */
	private static function _getImagePixelsArray($imagePath)
	{
		$imginfo = getimagesize($imagePath);
		$srcImage = imagecreatefrompng($imagePath);

		if(!$srcImage) {
			throw new Exception('Input image open failed');
		}

		$imageInfo = array(
			'width' => $imginfo[0],
			'height' => $imginfo[1],
			'type' => $imginfo[2],
			'bits' => $imginfo['bits'],
		);

		$imagePixelsArray = array();

		//人民币符号及左侧空格宽度
		$marginLeft = self::$config['marginLeft'];
		//顶部空格
		$marginTop = self::$config['marginTop'];
		$numberHeight = self::$config['numberHeight'];

		for($width = $marginLeft; $width < $imageInfo['width']; $width++){
			$line = array();
			for($height = $marginTop; $height < $marginTop + $numberHeight; $height++){
				$line[] = self::_getRgbHexColor(ImageColorAt($srcImage, $width, $height));
			}
			$imagePixelsArray[] = $line;
		}

		//移除右侧空白
		foreach($imagePixelsArray as $line => $lineArray) {
			if(self::_isBlankLine($lineArray) === false) {
				$lastNotBlankLine = $line; 
			}
		}
		$endLine = $lastNotBlankLine + 1;
		$imagePixelsArrayCount = count($imagePixelsArray);
		for($i = $endLine + 1; $i < $imagePixelsArrayCount; $i++){
			unset($imagePixelsArray[$i]);
		}

		return $imagePixelsArray;
	}

    /**
	 * 得到数字整数部分的位数
     * 
     * @param array
     * @return int
     */
	private static function _getNumberDigits($imagePixelsArray)
	{
		if(!$imagePixelsArray) {
			return 0;
		}

		$imagePixelsWidth = count($imagePixelsArray);
		$numberWidth = self::$config['numberWidth'];
		$pointWidth = self::$config['pointWidth'];
		$spacing = self::$config['spacing'];

		//移除最后的小数点及后两位
		$digits = ($imagePixelsWidth - $numberWidth * 2 - $pointWidth - $spacing * 2) / ($numberWidth + $spacing);	

		return $digits;
	}


    /**
	 * 判断一列是否为全白
     * 
     * @param array
     * @return boolean
     */
	private static function _isBlankLine($lineArray, $blankColor = "FFFFFF")
	{
		//干扰码转为白色
		foreach($lineArray as $key => $color){
			if(self::_isRed($color) === false) {
				$lineArray[$key] = $blankColor;
			}
		}
		foreach($lineArray as $color){
			if($color !== $blankColor) {
				return false;
			}
		}
		return true;	
	}


    /**
	 * 转换GD解析的颜色值为RGB数组 (0-255)
	 * 
     * 
     * @param int
     * @return array array(r, g, b)
     */
	private static function _getRgbColor($rgb)
	{
		return array(
			($rgb >> 16) & 0xFF,
			($rgb >> 8) & 0xFF,
			$rgb & 0xFF,
		);	
	}

    /**
	 * 转换RGB数组为16进制颜色
	 * 
     * 
     * @param array
     * @return string FFFFFF
     */
	private static function _getRgbHexColor($rgb)
	{
		$rgb = self::_getRgbColor($rgb);

		$rgbHex = '';

		foreach($rgb as $color)
		{
			$rgbHex .= str_pad(strtoupper(dechex($color)), 2, '0');
		}

		return $rgbHex;
	}


    /**
	 * 判断一个颜色是否为红色的近似色
	 * 
     * 
     * @param string
     * @return boolean
     */
	private static function _isRed($hexColorString)
	{
		$colors = str_split($hexColorString, 2);

		foreach($colors as $key => $color){
			$colors[$key] = hexdec($color);
		}

		if($colors[0] - $colors[2] > 100 && $colors[0] - $colors[1] > 100) {
			return true;
		}

		return false;
	}


    /**
	 * 将一个数字数组转换为字符串
	 * 
     * 
     * @param array
     * @return string
     */
	private static function _numberArrayToString($numberArray)
	{
		$numberString = '';
		$lines = array();
		foreach($numberArray as $lineArray) {
			$lines[] = implode('', $lineArray);
		}
		return implode('', $lines);
	}

    /**
	 * 将一个数字字符串转换为数组
	 * 
     * 
     * @param string
     * @return array
     */
	private static function _stringToNumberArray($numberString)
	{
		$numberArray = array();
		$width = self::$config['numberWidth'];
		$height = self::$config['numberHeight'];

		for($i = 0; $i < $width; $i++) {

			for($j = 0; $j < $height; $j++) {
				$numberArray[$i][] = $numberString{$i * $height + $j};
			}
		
		}

		return $numberArray;
	}

    /**
	 * 将一个数字颜色数组转换为0或1的bit数组
	 * 1为红色，0为白色
	 * 
     * 
     * @param array
     * @return array
     */
	private static function _numberArrayToBitArray($numberArray)
	{
		foreach($numberArray as $line => $lineColorArray) {
			foreach($lineColorArray as $bit => $color){
				$numberArray[$line][$bit] = self::_isRed($color) ? '1' : '0';
			}
		}

		return $numberArray;	
	}

    /**
	 * 获得单个标准数字数组
	 * 
     * 
     * @param int
     * @return array
     */
	private static function _getStandardNumberArray($number)
	{
		$path = realpath(dirname(__FILE__)) . self::$config['standardNumberPath'];
		$imagePath = $path . 's' . (string) $number . '.png';
		if(!file_exists($imagePath)){
			throw new Exception('standard number file no found');
		}
		$img = imagecreatefrompng($imagePath);


		$numberArray = array();
		for($width = 0; $width < 10; $width++) {
			$line = array();
			for($height = 0; $height < 11; $height++) {
				$line[] = self::_getRgbHexColor(ImageColorAt($img, $width, $height));
			}

			$numberArray[] = $line;
		}

		return $numberArray;
	}

    /**
	 * 获得所有标准数字，相似度比较依据
	 * 
     * 
     * @param int
     * @return array
     */
	private static function _getStandardNumbers()
	{
		$path = realpath(dirname(__FILE__)) . self::$config['standardNumberPath'];
		if(!is_writable($path)) {
			throw new Exception('Ocr cache dir not writable');
		}
		$cachefile = $path . 'numbers.php';

		if(self::$debug === true) {
			echo '<pre>Standard Numbers</pre>';
			for($i = 0; $i <= 9; $i++){
				self::printNumberArray(
					self::_numberArrayToBitArray(
						self::_getStandardNumberArray($i)
					)
				);
			}
		}

		if(file_exists($cachefile)) {
			$standardNumbers = unserialize(file_get_contents($cachefile));
			return $standardNumbers;
		}

		$standardNumbers = array();
		
		for($i = 0; $i <= 9; $i++){
			$standardNumbers[] = self::_numberArrayToString(
				self::_numberArrayToBitArray(
					self::_getStandardNumberArray($i)
				)
			);
		}

		Zend_Easthv_File::writeFile($cachefile, serialize($standardNumbers));
		return $standardNumbers;
	}

    /**
	 * 初始化，检测GD，获得标准数字
	 * 
     */
	public static function init()
	{
		if(!function_exists('imagecreatefrompng')) {
			throw new Exception('OCR function required GD image library');
		}	

		self::$_standardNumbers = self::_getStandardNumbers();
	}

}
